/*******************************************************************************
 * Copyright (c) 2013-2017 Lorenzo Bettini.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Lorenzo Bettini - Initial contribution and API
 *******************************************************************************/

/*
 * generated by Xtext
 */
package org.eclipse.xsemantics.example.fj.scoping;

import org.eclipse.xsemantics.example.fj.fj.Class;
import org.eclipse.xsemantics.example.fj.fj.ClassType;
import org.eclipse.xsemantics.example.fj.fj.Expression;
import org.eclipse.xsemantics.example.fj.fj.Member;
import org.eclipse.xsemantics.example.fj.fj.Selection;
import org.eclipse.xsemantics.example.fj.typing.FjTypeSystem;
import org.eclipse.xsemantics.example.fj.util.FjTypeUtils;
import org.eclipse.xsemantics.runtime.RuleEnvironment;
import org.eclipse.xsemantics.runtime.RuleFailedException;

import java.util.LinkedList;
import java.util.List;

import org.eclipse.emf.ecore.EReference;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.scoping.Scopes;
import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider;

import com.google.inject.Inject;

/**
 * This class contains custom scoping description.
 * 
 * see : http://www.eclipse.org/Xtext/documentation/latest/xtext.html#scoping on
 * how and when to use it
 * 
 */
public class FJScopeProvider extends AbstractDeclarativeScopeProvider {

	@Inject
	FjTypeSystem typeSystem;

	@Inject
	FjTypeUtils fjTypeUtils;

	// @Override
	// public IScope getScope(EObject context, EReference reference) {
	// if (reference == FjPackage.Literals.SELECTION__MESSAGE) {
	// if (context instanceof Selection) {
	// Selection selection = (Selection) context;
	// return Scopes.scopeFor(getMembers(getExpressionClass(selection
	// .getReceiver())));
	// }
	// return IScope.NULLSCOPE;
	// }
	//
	// return super.getScope(context, reference);
	// }

	public IScope scope_Member(Selection sel, EReference ref) {
		return Scopes
				.scopeFor(getMembers(getExpressionClass(sel.getReceiver())));
	}

	protected Class getExpressionClass(Expression receiver) {
		ClassType classType = typeSystem.classtype(
				environmentForExpression(receiver), receiver).getValue();
		return (classType != null ? classType.getClassref() : null);
	}

	private RuleEnvironment environmentForExpression(Expression expression) {
		return fjTypeUtils.environmentWithMappedThisAsContainingClass(expression);
	}


	public List<Member> getMembers(Class cl) {
		List<Member> allMembers = new LinkedList<Member>();
		try {
			allMembers.addAll(typeSystem.fields(cl));
			allMembers.addAll(typeSystem.methods(cl));
		} catch (RuleFailedException e) {
			// the list will be empty
		}
		return allMembers;
	}
}
